;;
;; Licensed to the .NET Foundation under one or more agreements.
;; The .NET Foundation licenses this file to you under the MIT license.
;;

include AsmOffsets.inc      ; generated by the build from AsmOffsets.cpp

;;
;; MACROS
;;

;
; Define macros to build unwind data for prologues.
;

push_nonvol_reg macro Reg

        .errnz ___STACK_ADJUSTMENT_FORBIDDEN, <push_nonvol_reg cannot be used after save_reg_postrsp>

        push    Reg
        .pushreg Reg

        endm

push_vol_reg macro Reg

        .errnz ___STACK_ADJUSTMENT_FORBIDDEN, push_vol_reg cannot be used after save_reg_postrsp

        push    Reg
        .allocstack 8

        endm

push_imm macro imm

        .errnz ___STACK_ADJUSTMENT_FORBIDDEN, push_vol_reg cannot be used after save_reg_postrsp

        push    imm
        .allocstack 8

        endm

push_eflags macro

        .errnz ___STACK_ADJUSTMENT_FORBIDDEN, push_eflags cannot be used after save_reg_postrsp

        pushfq
        .allocstack 8

        endm

alloc_stack macro Size

        .errnz ___STACK_ADJUSTMENT_FORBIDDEN, alloc_stack cannot be used after save_reg_postrsp

        sub     rsp, Size
        .allocstack Size

        endm

save_reg_frame macro Reg, FrameReg, Offset

        .erre ___FRAME_REG_SET, save_reg_frame cannot be used before set_frame

        mov     Offset[FrameReg], Reg
        .savereg Reg, Offset

        endm

save_reg_postrsp macro Reg, Offset

        .errnz ___FRAME_REG_SET, save_reg_postrsp cannot be used after set_frame

        mov     Offset[rsp], Reg
        .savereg Reg, Offset

        ___STACK_ADJUSTMENT_FORBIDDEN = 1

        endm

save_xmm128_frame macro Reg, FrameReg, Offset

        .erre ___FRAME_REG_SET, save_xmm128_frame cannot be used before set_frame

        movdqa  Offset[FrameReg], Reg
        .savexmm128 Reg, Offset

        endm

save_xmm128_postrsp macro Reg, Offset

        .errnz ___FRAME_REG_SET, save_reg_postrsp cannot be used after set_frame

        movdqa  Offset[rsp], Reg
        .savexmm128 Reg, Offset

        ___STACK_ADJUSTMENT_FORBIDDEN = 1

        endm

set_frame macro Reg, Offset

        .errnz ___FRAME_REG_SET, set_frame cannot be used more than once

if Offset

        lea     Reg, Offset[rsp]

else

        mov     reg, rsp

endif

        .setframe Reg, Offset
        ___FRAME_REG_SET = 1

        endm

END_PROLOGUE macro

        .endprolog

        endm

;
; Define function entry/end macros.
;

LEAF_ENTRY macro Name, Section

Section segment para 'CODE'

        align   16

        public  Name
Name    proc

        endm

LEAF_END macro Name, section

Name    endp

Section ends

        endm

LEAF_END_MARKED macro Name, section
        public Name&_End
Name&_End label qword
        ; this nop is important to keep the label in
        ; the right place in the face of BBT
        nop

Name    endp

Section ends

        endm


NESTED_ENTRY macro Name, Section, Handler

Section segment para 'CODE'

        align   16

        public  Name

ifb <Handler>

Name    proc    frame

else

Name    proc    frame:Handler

endif

        ___FRAME_REG_SET = 0
        ___STACK_ADJUSTMENT_FORBIDDEN = 0

        endm

NESTED_END macro Name, section

Name    endp

Section ends

        endm

NESTED_END_MARKED macro Name, section
        public Name&_End
Name&_End label qword

Name    endp

Section ends

        endm


ALTERNATE_ENTRY macro Name

Name label proc
PUBLIC Name
        endm

LABELED_RETURN_ADDRESS macro Name

Name label proc
PUBLIC Name
        endm

_tls_array     equ 58h     ;; offsetof(TEB, ThreadLocalStoragePointer)

;;
;; __declspec(thread) variable
;;
INLINE_GET_TLS_VAR macro destReg, trashReg, variable
    EXTERN _tls_index : DWORD
    EXTERN variable:DWORD

;;
;; construct 'eax' from 'rax' so that the register size and data size match
;;
;; BEWARE: currently only r10 is allowed as destReg from the r8-r15 set.
;;
ifidni <destReg>, <r10>
destRegDWORD EQU r10d
else
destRegDWORD TEXTEQU @CatStr( e, @SubStr( destReg, 2, 2 ) )
endif

    mov         destRegDWORD, [_tls_index]
    mov         trashReg, gs:[_tls_array]
    mov         trashReg, [trashReg + destReg * 8]
    mov         destRegDWORD, SECTIONREL variable
    add         destReg, trashReg

endm

;;
;; __declspec(thread) tls_CurrentThread
;;
INLINE_GETTHREAD macro destReg, trashReg
    INLINE_GET_TLS_VAR destReg, trashReg, tls_CurrentThread
endm

INLINE_THREAD_UNHIJACK macro threadReg, trashReg1, trashReg2
        ;;
        ;; Thread::Unhijack()
        ;;
        mov         trashReg1, [threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress]
        cmp         trashReg1, 0
        je          @F

        mov         trashReg2, [threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation]
        mov         [trashReg2], trashReg1
        mov         qword ptr [threadReg + OFFSETOF__Thread__m_ppvHijackedReturnAddressLocation], 0
        mov         qword ptr [threadReg + OFFSETOF__Thread__m_pvHijackedReturnAddress], 0

@@:
endm

DEFAULT_FRAME_SAVE_FLAGS equ PTFF_SAVE_ALL_PRESERVED + PTFF_SAVE_RSP

;;
;; Macro used from unmanaged helpers called from managed code where the helper does not transition immediately
;; into pre-emptive mode but may cause a GC and thus requires the stack is crawlable. This is typically the
;; case for helpers that meddle in GC state (e.g. allocation helpers) where the code must remain in
;; cooperative mode since it handles object references and internal GC state directly but a garbage collection
;; may be inevitable. In these cases we need to be able to transition to pre-meptive mode deep within the
;; unmanaged code but still be able to initialize the stack iterator at the first stack frame which may hold
;; interesting GC references. In all our helper cases this corresponds to the most recent managed frame (e.g.
;; the helper's caller).
;;
;; This macro builds a frame describing the current state of managed code.
;;
;; INVARIANTS
;; - The macro assumes it is called from a prolog, prior to a frame pointer being setup.
;; - All preserved registers remain unchanged from their values in managed code.
;;
PUSH_COOP_PINVOKE_FRAME macro trashReg
    lea             trashReg, [rsp +  8h]
    push_vol_reg    trashReg                    ; save caller's RSP
    push_nonvol_reg r15                         ; save preserved registers
    push_nonvol_reg r14                         ;   ..
    push_nonvol_reg r13                         ;   ..
    push_nonvol_reg r12                         ;   ..
    push_nonvol_reg rdi                         ;   ..
    push_nonvol_reg rsi                         ;   ..
    push_nonvol_reg rbx                         ;   ..
    push_imm        DEFAULT_FRAME_SAVE_FLAGS    ; save the register bitmask
    push_vol_reg    trashReg                    ; Thread * (unused by stackwalker)
    push_nonvol_reg rbp                         ; save caller's RBP
    mov             trashReg, [rsp + 11*8]      ; Find the return address
    push_vol_reg    trashReg                    ; save m_RIP
    lea             trashReg, [rsp + 0]         ; trashReg == address of frame

    ;; allocate scratch space and any required alignment
    alloc_stack     28h
endm

;;
;; Pop the frame and restore register state preserved by PUSH_COOP_PINVOKE_FRAME
;;
POP_COOP_PINVOKE_FRAME macro
    add         rsp, 30h
    pop         rbp     ; restore RBP
    pop         r10     ; discard thread
    pop         r10     ; discard bitmask
    pop         rbx
    pop         rsi
    pop         rdi
    pop         r12
    pop         r13
    pop         r14
    pop         r15
    pop         r10     ; discard caller RSP
endm

;  - TAILCALL_RAX: ("jmp rax") should be used for tailcalls, this emits an instruction
;            sequence which is recognized by the unwinder as a valid epilogue terminator
TAILJMP_RAX TEXTEQU <DB 048h, 0FFh, 0E0h>

;;
;; CONSTANTS -- INTEGER
;;
TSF_Attached                    equ 01h
TSF_SuppressGcStress            equ 08h
TSF_DoNotTriggerGc              equ 10h

;;
;; Rename fields of nested structs
;;
OFFSETOF__Thread__m_alloc_context__alloc_ptr        equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_ptr
OFFSETOF__Thread__m_alloc_context__alloc_limit      equ OFFSETOF__Thread__m_rgbAllocContextBuffer + OFFSETOF__gc_alloc_context__alloc_limit



;; GC type flags
GC_ALLOC_FINALIZE               equ 1

;; Note: these must match the defs in PInvokeTransitionFrameFlags
PTFF_SAVE_RBX           equ 00000001h
PTFF_SAVE_RSI           equ 00000002h
PTFF_SAVE_RDI           equ 00000004h
PTFF_SAVE_R12           equ 00000010h
PTFF_SAVE_R13           equ 00000020h
PTFF_SAVE_R14           equ 00000040h
PTFF_SAVE_R15           equ 00000080h
PTFF_SAVE_ALL_PRESERVED equ 000000F7h   ;; NOTE: RBP is not included in this set!
PTFF_SAVE_RSP           equ 00008000h
PTFF_SAVE_RAX           equ 00000100h   ;; RAX is saved in hijack handler - in case it contains a GC ref
PTFF_SAVE_ALL_SCRATCH   equ 00007F00h
PTFF_RAX_IS_GCREF       equ 00010000h   ;; iff PTFF_SAVE_RAX: set -> eax is Object, clear -> eax is scalar
PTFF_RAX_IS_BYREF       equ 00020000h   ;; iff PTFF_SAVE_RAX: set -> eax is ByRef, clear -> eax is Object or scalar
PTFF_THREAD_ABORT       equ 00100000h   ;; indicates that ThreadAbortException should be thrown when returning from the transition

;; These must match the TrapThreadsFlags enum
TrapThreadsFlags_None            equ 0
TrapThreadsFlags_AbortInProgress equ 1
TrapThreadsFlags_TrapThreads     equ 2

;; This must match HwExceptionCode.STATUS_REDHAWK_THREAD_ABORT
STATUS_REDHAWK_THREAD_ABORT      equ 43h

;;
;; CONSTANTS -- SYMBOLS
;;

ifdef FEATURE_GC_STRESS
THREAD__HIJACKFORGCSTRESS                   equ ?HijackForGcStress@Thread@@SAXPEAUPAL_LIMITED_CONTEXT@@@Z
endif ;; FEATURE_GC_STRESS

;;
;; IMPORTS
;;

EXTERN RhpGcAlloc                               : PROC
EXTERN RhpValidateExInfoPop                     : PROC
EXTERN RhDebugBreak                             : PROC
EXTERN RhpWaitForGC2                            : PROC
EXTERN RhExceptionHandling_FailedAllocation     : PROC
EXTERN RhThrowHwEx                              : PROC
EXTERN RhThrowEx                                : PROC
EXTERN RhRethrow                                : PROC
EXTERN RhpGcPoll2                               : PROC
ifdef FEATURE_GC_STRESS
EXTERN RhpStressGc                              : PROC
EXTERN THREAD__HIJACKFORGCSTRESS                : PROC
endif ;; FEATURE_GC_STRESS

EXTERN RhpTrapThreads       : DWORD
EXTERN g_lowest_address     : QWORD
EXTERN g_highest_address    : QWORD
EXTERN g_ephemeral_low      : QWORD
EXTERN g_ephemeral_high     : QWORD
EXTERN g_card_table         : QWORD

ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
EXTERN g_card_bundle_table  : QWORD
endif

ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
EXTERN g_write_watch_table  : QWORD
endif
